home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / scott / WWW / NextStep / Implementation / old / FileAccess.m < prev    next >
Text File  |  1992-11-25  |  16KB  |  644 lines

  1. //        File Access                    FileAccess.m
  2. //        ===========
  3. //
  4. // A HyperAccess object provides access to hyperinformation, using particular
  5. // protocols and data format transformations. This one provides access to
  6. // files in mounted filesystems.
  7.  
  8. #define takeBackup YES        // Take backup when saving existing file Cmd/S
  9.  
  10. #import "FileAccess.h"
  11. #import <appkit/appkit.h>
  12. #import <stdio.h>
  13. #import <sys/types.h>
  14. #import <sys/stat.h>        // For fstat()
  15. #import <libc.h>        // For getgroups()
  16. #import "HTUtils.h"
  17. #import "WWW.h"            // For WWW_ constants etc
  18. #import "HTParse.h"
  19. #import "HTTCP.h"        // TCP utilities, like getting host name
  20. #import "HTFTP.h"        // FTP protocol routines
  21. #import "HTFile.h"        // File access routines
  22.  
  23.  
  24. #define THIS_TEXT  (HyperText *)[[[NXApp mainWindow] contentView] docView]
  25.  
  26. @implementation FileAccess : HyperAccess
  27.  
  28. #define LENGTH 256
  29. extern char * appDirectory;        /* Pointer to directory for application */
  30.  
  31.  
  32. //    Initialize this class
  33. //
  34. + initialize
  35. {
  36.     return [super initialize];
  37. }
  38.  
  39.  
  40. //    Overlay method returning the name of the access.
  41.  
  42. - (const char *)name
  43. {
  44.     return "file";
  45. }
  46.  
  47. //////////////////////////////////////////////////////////////////////////////////////
  48.  
  49. //                    S A V I N G
  50.  
  51. //    Save as an HTML file of given name (any format)
  52. //    ----------------------------------
  53. //
  54.  
  55.  
  56. - save: (HyperText *)HT inFile:(const char *)filename format:(int)format
  57. {
  58.     NXStream * s;                //    The file stream
  59.     
  60.     s = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  61.  
  62.     if (format==WWW_HTML) {
  63.         char *saveName = WWW_nameOfFile(filename);    //    The WWW address
  64.         [HT writeSGML:s relativeTo:saveName];
  65.         free(saveName);
  66.     }
  67.     else if (format==WWW_RICHTEXT) [HT writeRichText:s];
  68.     else if (format==WWW_PLAINTEXT || format==WWW_SOURCE) [HT writeText:s];
  69.     else fprintf(stderr, "HT/File: Unknown format!\n"); 
  70.     
  71.     if (TRACE) printf("HT file: %li bytes in file `%s' format %i.\n",
  72.         NXTell(s), filename, format);
  73.     
  74.     NXFlush(s);            /* Try to get over missing end */
  75.     NXSaveToFile(s, filename);
  76.     NXCloseMemory(s, NX_FREEBUFFER);
  77.     return self;
  78. }
  79.  
  80.  
  81. //    Get Filename near a "hint" node
  82. //    -------------------------------
  83. //
  84. // On entry,
  85. //    hint        is a hypertext whose name is to be used as a hint for
  86. //            the user when selecting the name.
  87. // On exit,
  88. //    returns        0 if no file selected
  89. //            pointer to static string with filename if ok
  90. //
  91. const char * ask_name(HyperText * hint, int format)
  92. {
  93.     char *         suggestion;
  94.     char *         slash;
  95.     int            status;
  96.     static SavePanel *     save_panel;        /* Keep a Save panel alive  */
  97.     
  98.     if (!hint) return 0;
  99.     if (!save_panel) {
  100.         save_panel = [SavePanel new];    //    Keep between invocations
  101.     }
  102.     
  103.     if (format==WWW_HTML) [save_panel setRequiredFileType: "html"];
  104.     else if (format==WWW_RICHTEXT) [save_panel setRequiredFileType: "rtf"];
  105.     else if (format==WWW_PLAINTEXT) [save_panel setRequiredFileType: "txt"];
  106.     else if (format==WWW_POSTSCRIPT) [save_panel setRequiredFileType: "ps"];
  107.     else [save_panel setRequiredFileType: ""];
  108.     
  109.     suggestion = HTLocalName([[hint nodeAnchor]address]);
  110.     slash = strrchr(suggestion, '/');    //    Point to last slash
  111.     if (slash) {
  112.         *slash++ = 0;            /* Separate directory and filename */
  113.     status = [save_panel runModalForDirectory:suggestion file:slash];
  114.     } else {
  115.         if (TRACE) printf ("No slash in directory!!\n");
  116.     status = [save_panel runModalForDirectory:"." file:suggestion];
  117.     }
  118.     free(suggestion);
  119.     
  120.     if (!status) {
  121.         if (TRACE) printf("Save cancelled.\n");
  122.     return 0;
  123.     }
  124.     return [save_panel filename];
  125. }
  126.  
  127.  
  128. //    Save as a file    using save panel    (any format)
  129. //    --------------------------------
  130. // On entry,
  131. //    format    Gives the file format to be used (see WWW.h)
  132. //    hint    is a node to be used for a suggested name.
  133. //
  134. - saveIn:(int)format like:(HyperText *)hint
  135. {  
  136.     const char * filename;        //    The name of the file selected
  137.     HyperText * HT;
  138.     
  139.     HT = THIS_TEXT;
  140.     if (!HT) return HT;            //    No main window!
  141.     
  142.     filename = ask_name(HT, format);
  143.     if (!filename) return nil;        //    Save clancelled.
  144.     
  145.     return [self save:HT inFile:filename format:format]; 
  146. }
  147.  
  148. //    Save as an HTML file    using save panel
  149. //    --------------------
  150. //
  151. - saveAs:sender
  152. {
  153.     return [self saveIn:WWW_HTML like:THIS_TEXT];
  154. }
  155.  
  156. //    Save as an RTF file    using save panel
  157. //    --------------------
  158. //
  159. - saveAsRichText:sender
  160. {
  161.     return [self saveIn:WWW_RICHTEXT like:THIS_TEXT];
  162. }
  163.  
  164. //    Save as a plain text file    using save panel
  165. //    -------------------------
  166. //
  167. - saveAsPlainText:sender
  168. {
  169.     return [self saveIn:WWW_PLAINTEXT like:THIS_TEXT];
  170. }
  171.  
  172.  
  173. //    Save as an HTML file of same name
  174. //    ---------------------------------
  175. //
  176. //    We don't use a suffix for the backup filename, because some file systems
  177. //    (which may be NFS mounted) truncate filenames and can chop the suffix off!
  178. //
  179. - saveNode:(HyperText *) HT
  180. {  
  181.     char * filename;        //    The name chosen
  182.     id        status;
  183.     FILE * fp;
  184.     const char * addr = [[HT nodeAnchor]address];
  185.  
  186.     filename = HTLocalName(addr);
  187.  
  188. /*    Take a backup before we do anything silly
  189. */        
  190.     if (takeBackup) {
  191.         char * backup_filename = 0;
  192.     char * p = backup_filename;
  193.     backup_filename = malloc(strlen(filename)+2);
  194.     strcpy(backup_filename, filename);
  195.     for(p=backup_filename+strlen(backup_filename);; p--) {// Start at null
  196.         if ((*p=='/') || (p<backup_filename)) {
  197.             p[1]=',';        /* Insert comma after slash */
  198.         break;
  199.         }
  200.         p[1] = p[0];    /* Move up everything to the right of it */
  201.     }
  202.     
  203.     if (fp=fopen(filename, "r")) {            /* File exists */
  204.         fclose(fp);
  205.         if (TRACE) printf("File `%s' exists\n", filename);
  206.         if (remove(backup_filename)) {
  207.         if (TRACE) printf("Backup file `%s' removed\n",
  208.              backup_filename);
  209.         }
  210.         if (rename(filename, backup_filename)) {    /* != 0 => Failure */
  211.         if (TRACE) printf("Rename `%s' to `%s' FAILED!\n",
  212.                     filename, backup_filename);
  213.         } else {                    /* Success */
  214.         if (TRACE) printf("Renamed `%s' to `%s'\n", filename,
  215.                 backup_filename);
  216.         }
  217.     }
  218.         free(backup_filename);
  219.     } /* if take backup */    
  220.     
  221.  
  222.     status = [self save:HT inFile:filename format:[HT format]];
  223.     if (status) [[HT window] setDocEdited: NO];
  224.     free(filename);
  225.     return status;
  226. }
  227.  
  228. //////////////////////////////////////////////////////////////////////////////////
  229. //
  230. //                O P E N I N G
  231.  
  232.  
  233. //            LOAD ANCHOR
  234. //
  235. //    Returns    If successful, the anchor of the node loaded or found.
  236. //        If failed, nil.
  237.  
  238. - loadAnchor: (Anchor *) anAnchor Diagnostic:(int)diagnostic
  239. {
  240.     const char * addr = [anAnchor address];
  241.     
  242.     NXStream * s;            //    The file stream
  243.     HyperText * HT;            //    The new node
  244.     char * filename;            //    The name of the file
  245.     char * newname =  0;        //    The simplified name
  246.     int format;                //    The file format
  247.     int file_number = -1;        //    File number if FTP
  248.  
  249. //    First, we TRY to reduce the name to a unique one.
  250.  
  251.     if (!addr) return nil;
  252.     StrAllocCopy(newname, addr);
  253.     HTSimplify(newname);
  254.     [anAnchor setAddress:newname];
  255.     filename = HTLocalName(newname);
  256.     addr = [anAnchor address];
  257.     
  258.     if ([anAnchor node]) {
  259.         if (TRACE) printf("Anchor %p already has a node\n", anAnchor);
  260.     
  261.     } else {
  262.     s = NXMapFile(filename, NX_READONLY);    // Map file into memory
  263.     
  264.     if (!s) {
  265.         if (TRACE) printf("Could not open file `%s', errno=%i.\n",
  266.             filename, errno);
  267.         file_number = HTFTP_open_file_read(newname);    // Try FTP
  268.         if (file_number>=0) {
  269.             s = NXOpenFile(file_number, NX_READONLY);
  270.         }
  271.     }
  272.     
  273.     if (!s) {
  274.         if (TRACE) printf("Could not open `%s' with FTP either.\n",
  275.             newname);
  276.         NXRunAlertPanel(NULL, "Could not open `%s'\n",
  277.             NULL,NULL,NULL,
  278.         newname   /*, strerror(errno) */ );
  279.         free(filename);
  280.             free(newname);
  281.         return nil;
  282.     }
  283.         free(newname);
  284.  
  285.  
  286. //    For unrecognised types, open in the Workspace:
  287.     
  288.     format = HTFileFormat(filename);
  289.     if (format == WWW_INVALID) {
  290.             
  291.         char command[255];            /* Open in the workspace */
  292.         sprintf(command, "open %s", filename);
  293.         system(command);
  294.     
  295.     } else {    /* Known format */
  296.     
  297. //    Make the new node:
  298.  
  299.         HT = [HyperText newAnchor:anAnchor Server:self];
  300.         [HT setupWindow];            
  301.         [[HT window]setTitle:filename];     // Show something's happening
  302.  
  303.         if (file_number<0)
  304.             [HT setEditable:HTEditable(filename)]; // This is editable?
  305.         else
  306.             [HT setEditable: NO];            // AFTP data.
  307.         
  308.         switch(format) {
  309.         case WWW_HTML:
  310.             if (diagnostic==2) {
  311.                 [HT readText:s];
  312.                 [HT setFormat: WWW_SOURCE]; 
  313.             } else  {  
  314.                 [HT readSGML:s diagnostic:diagnostic];
  315.             }
  316.             break;
  317.             
  318.         case WWW_RICHTEXT:   
  319.             if (diagnostic > 0) {
  320.                 [HT readText:s];
  321.                 [HT setFormat: WWW_SOURCE]; 
  322.             } else {   
  323.                 [HT readRichText:s];
  324.             }
  325.             break;
  326.             
  327.         case WWW_PLAINTEXT:
  328.             [HT readText:s];
  329.                 break;
  330.         }
  331.     }
  332.     
  333. //    Clean up now it's on the screen:
  334.     
  335.     if (TRACE) printf("Closing file stream\n");
  336.     if (file_number>=0) {
  337.         NXClose(s);
  338.         HTFTP_close_file(file_number);
  339.     } else {
  340.         NXCloseMemory(s, NX_FREEBUFFER);
  341.     }
  342.     
  343.     } /* If anAnchor not loaded before */
  344.  
  345.     free(filename);
  346.     return anAnchor;
  347. }
  348.  
  349.  
  350. //    Open a file of a given name
  351. //    ---------------------------
  352. //
  353. //
  354. // On exit,
  355. //    Returns    If successful, the anchor of the node loaded or found.
  356. //        If failed, nil.
  357.  
  358. - openFile:(const char *)filename diagnostic:(BOOL)diagnostic
  359. {
  360.     Anchor * a;
  361.     char * node_address = WWW_nameOfFile(filename);
  362.     a = [self loadAnchor:[Anchor newAddress:node_address]
  363.              Diagnostic:diagnostic];
  364.     free(node_address);
  365.     return a;
  366. }
  367.  
  368.  
  369. //    Ask the User for the name of a file
  370. //    -----------------------------------
  371. //
  372.  
  373. const char * existing_filename()
  374. {
  375.     HyperText * HT;            //    The new node
  376.     char * suggestion = 0;
  377.     char * slash;
  378.     int    status;
  379.     char * typelist = 0;        //    Any extension.
  380.     static OpenPanel * openPanel=0;
  381.  
  382.  
  383. //    Get The Filename from the User
  384.     
  385.     if (!openPanel) {
  386.         openPanel = [OpenPanel new];
  387.         [openPanel allowMultipleFiles:NO];
  388.     }
  389.  
  390.     HT = THIS_TEXT;            // Hypertext in main window, if selected.
  391.     if (HT) {
  392.     suggestion = HTLocalName([[HT nodeAnchor]address]);
  393.     slash = strrchr(suggestion, '/');    //    Point to last slash
  394.     if (slash) {
  395.         *slash++ = 0;            /* Separate directory and filename */
  396.         status = [openPanel runModalForDirectory:suggestion
  397.                     file:""
  398.                 types:&typelist];
  399.         // (was: file:slash but this is silly as that is already open.)
  400.     } else {
  401.             if (TRACE) printf ("No slash in directory!!\n");
  402.         status = [openPanel runModalForDirectory:"."
  403.             file:"" types:&typelist];
  404.         // (was: file:suggestion but this is silly as above)
  405.     }
  406.     free(suggestion);
  407.     } else {
  408.         status = [openPanel runModalForTypes:&typelist];
  409.     }
  410.  
  411.     if (!status) {
  412.         if (TRACE) printf("Open cancelled\n");
  413.     return NULL;
  414.     }
  415.     return [openPanel filename];
  416. }
  417.  
  418.  
  419. //    Link to a given file
  420. //    --------------------
  421.  
  422. - linkToFile:sender
  423. {
  424.     const char * filename = existing_filename();    // Ask for filename
  425.     if (filename) {
  426.         char * node_address = WWW_nameOfFile(filename);
  427.         Anchor * a =  [Anchor newAddress:node_address];
  428.     free(node_address);
  429.     
  430.     return [THIS_TEXT linkSelTo:a];
  431.     }
  432.     return nil;
  433. }
  434.  
  435.  
  436. //    Open A File using the panel            openDiagnostic:
  437. //    ---------------------------
  438. //
  439. // On exit,
  440. //    Returns    If successful, the anchor of the node loaded or found.
  441. //        If failed, nil.
  442.  
  443. - openDiagnostic:(int)diagnostic
  444. {
  445.     
  446.     const char * filename = existing_filename();
  447.  
  448.     if (!filename) {
  449.         if (TRACE) printf("Open cancelled\n");
  450.     return nil;
  451.     }
  452.  
  453.     return [self openFile:filename diagnostic:diagnostic];
  454. }
  455.  
  456.  
  457.  
  458. //    Load a personal or system-wide version of a file
  459. //    ------------------------------------------------
  460. //
  461. //    This accesses the personal copy for the user or, failing that,
  462. //    for the system. It is important that this name is fully qualified
  463. //    as other names will be generated relative to it.
  464. //    We check whether the local version is there before loading it
  465. //    in order to prevent error messages being given to the user if
  466. //    it is not.
  467. //
  468. // On exit,
  469. //    Returns    If successful, the anchor of the node loaded or found.
  470. //        If failed, nil.
  471.  
  472. - openMy:(const char *)filename diagnostic:(int)diagnostic
  473. {
  474.     Anchor * a;
  475.     char name[256];
  476.     char template[256];
  477.     
  478.     if (getenv("HOME")) {
  479.     int status;
  480.     struct stat buf;            /* status buffer */
  481.     strcpy(name, getenv("HOME"));
  482.     strcat(name, "/WWW/");
  483.     strcat(name, filename);
  484.         status = stat(name, &buf);        /* Does file exist? */
  485.     if (status>=0) {            /* If it does, load it */
  486.         if (TRACE) printf("File: stat() returned %d\n", status);
  487.         a = [self openFile:name diagnostic:diagnostic];
  488.             if (a) {
  489.             if ([a node]) [a select];
  490.         return a;        /* Found local copy */
  491.         }
  492.         }
  493.     }
  494.  
  495.     strcpy(template, appDirectory);
  496.     strcat(template, filename);
  497.     a = [self openFile:template diagnostic:diagnostic];
  498.     if (!a) return nil;
  499.     
  500.     [a setAddress:name];        /* For later save back */
  501.     [[a node] setEditable:YES];        /* This is editable data? */
  502.     if ([a node]) [a select];        /* Bring to front */
  503.     return a;
  504. }
  505.  
  506.  
  507. //    Go Home
  508. //    -------
  509. //
  510. //    This accesses the default page of text for the user or, failing that,
  511. //    for the system. 
  512. //
  513. - goHome:sender
  514. {
  515.     return [self openMy:"default.html" diagnostic:0];
  516. }
  517.  
  518.  
  519. //    Make a new blank node named like the current node
  520. //    -------------------------------------------------
  521. //
  522. // On exit,
  523. //    Returns    If successful, the anchor of the node loaded or found.
  524. //        If failed, nil.
  525. - makeNew:sender
  526. {
  527.     id        status;
  528.     HyperText * hint = THIS_TEXT;    //    The old hypertext
  529.     char *    node_address;        //    of the new hypertext
  530.     Anchor *    a;            //    The new anchor
  531.     const char * filename;        //    The name of the file selected
  532.     HyperText * HT;            //    The new hypertext
  533.     
  534.     if (!hint) return nil;        //    No main window!
  535.  
  536.     a = [self openMy:"blank.html" diagnostic:0];
  537.     if (!a) return nil;            //    Couldn't find blank node
  538.     
  539.     [a select];
  540.     HT = [a node];
  541.     if (!HT) return HT;            //    No node?!
  542.     
  543.     filename = ask_name(hint, WWW_HTML);
  544.     if (!filename) return nil;        //    Save cancelled.
  545.     
  546.     node_address = WWW_nameOfFile(filename);
  547.     [a setAddress:node_address];    //     Adopt new address as node name
  548.  
  549.  
  550. /*    Make a default title for the document from the file name:
  551. **
  552. ** On entry,
  553. **    node_address must be valid and have a colon in it
  554. ** On exit,
  555. **    Title is set
  556. **    This is destructive of the node_address string.
  557. */
  558.     {
  559.     char * pDir = 0;            /* Last Directory name */
  560.         char * pTitle = strrchr(node_address, '/');    /* File name */
  561.  
  562.     if (pTitle) {
  563.         *pTitle++ = 0;            /* Chop in two */
  564.         pDir = strrchr(node_address, '/');
  565.         if (!pDir) pDir = strchr(node_address, ':');
  566.         pDir++;    /* After delimiter 921122 */
  567.     } else {
  568.         pTitle  = strrchr(node_address, ':')+1;
  569.         pDir = "";
  570.     }
  571.     if (strrchr(pTitle, '.')) *strrchr(pTitle, '.') = 0;// Kill filetype */
  572.  
  573.     if (pDir) {
  574.             char title[255];
  575.         sprintf(title, "%s -- %s", pTitle, pDir);
  576.         [HT setTitle:title];        /* Default title is filename */
  577.     } else {
  578.         [HT setTitle:pTitle];    
  579.     }
  580.     }
  581.     
  582.     free(node_address);
  583.     
  584.     status = [self save:HT inFile:filename format:WWW_HTML]; 
  585.     if (!status) return nil;        //    Save failed!
  586.     
  587.     [[a node] setEditable:YES];        // This is editable data.
  588.  
  589.     return a;
  590. }
  591.  
  592.  
  593. //    Make a new blank node named like the current node and link to it
  594. //    ----------------------------------------------------------------
  595. //
  596. - linkToNew:sender
  597. {
  598.     HyperText * HT = THIS_TEXT;
  599.     Anchor * a;
  600.     if (![HT isEditable]) return nil;        /* Won't be able to link to it */
  601.     
  602.     a = [self makeNew:sender];    /* Make the new node */
  603.     if (!a) return nil;
  604.     
  605.     return [HT linkSelTo:a];            /* Link the selection to it */
  606. }
  607.  
  608.  
  609. //        Actions:
  610. //
  611. - search:sender
  612. {
  613.   return nil;
  614. }
  615.  
  616. - searchRTF:sender
  617. {
  618.     return nil;
  619. }
  620.  
  621. - searchSGML:sender
  622. {
  623.     return nil;
  624. }
  625.  
  626. //    Direct open buttons:
  627.  
  628. - open:sender
  629. {
  630.    return [self openDiagnostic:0];
  631. }
  632.  
  633. - openRTF:sender
  634. {
  635.     return [self openDiagnostic:1];
  636. }
  637.  
  638. - openSGML:sender
  639. {
  640.     return [self openDiagnostic:2];
  641. }
  642.  
  643. @end
  644.